﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Minestorm.Retro.Entities
{
    public class Ship : Entity
    {
        private GraphicsDevice _graphics;
        private PointSpriteGenerator _pointSpriteGenerator;

        public Ship(GraphicsDevice graphics, Vector2 position, PointSpriteGenerator pointSpriteGenerator)
            : base(graphics, position)
        {
            Hidden = true;

            CanTeleport = true;

            IsAlive = true;

            _pointSpriteGenerator = pointSpriteGenerator;

            _graphics = graphics;

            EndTimespan = WinTimespan = TimeSpan.MaxValue;

            Mass = 10f;
        }

        public float Direction { get; set; }

        public Vector2 Velocity { get; set; }

        public bool IsThrusting { get; set; }

        public bool IsFiring { get; set; }

        public bool IsAlive { get; set; }

        public TimeSpan EndTimespan { get; set; }
        public TimeSpan WinTimespan { get; set; }

        public float Mass { get; set; }

        public override void Initialize()
        {
            var verticies = new VertexPositionColor[9];
            verticies[0] = new VertexPositionColor(new Vector3(-.75f, 0, 0), Constants.VectorColor);
            verticies[1] = new VertexPositionColor(new Vector3(-.75f, -1f, 0), Constants.VectorColor);
            verticies[2] = new VertexPositionColor(new Vector3(.25f, 0, 0), Constants.VectorColor);
            verticies[3] = new VertexPositionColor(new Vector3(0, 2f, 0), Constants.VectorColor);
            verticies[4] = new VertexPositionColor(new Vector3(-.25f, 0, 0), Constants.VectorColor);
            verticies[5] = new VertexPositionColor(new Vector3(.75f, -1f, 0), Constants.VectorColor);
            verticies[6] = new VertexPositionColor(new Vector3(.75f, 0, 0), Constants.VectorColor);

            SetVerticies(verticies);

            if (IsAlive)
                SetIndicies(new NamedShortArray("ship", false, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6));
            else
                SetIndicies(
                    new NamedShortArray("ship part 1", false, 0, 1, 1, 2),
                    new NamedShortArray("ship part 2a", false, 2, 3),
                    new NamedShortArray("ship part 2b", false, 3, 4),
                    new NamedShortArray("ship part 3", false, 4, 5, 5, 6)
                    );
        }

        private int _deadStep = 0;
        private Random _random = new Random();
        private float[] _partAngle = new float[4];

        public float GetVelocityAngle()
        {
            return (float)Math.Atan2((Double)Velocity.Y, (Double)Velocity.X);
        }

        public override Matrix GetWorld(string indexBufferName)
        {
            if (IsAlive)
            {
                return Matrix.Identity * Matrix.CreateScale(Constants.Ship.Scale) * Matrix.CreateRotationZ(Direction) * Matrix.CreateTranslation(new Vector3(Position, 0f));
            }
            else
            {
                _deadStep++;

                float partAngle = Direction;

                switch (indexBufferName)
                {
                    case "ship part 1":
                        partAngle = _partAngle[0];
                        break;
                    case "ship part 2a":
                        partAngle = _partAngle[1];
                        break;
                    case "ship part 2b":
                        partAngle = _partAngle[2];
                        break;
                    case "ship part 3":
                        partAngle = _partAngle[3];
                        break;
                }

                var dist = _deadStep * 0.005f;

                var partPosition = new Vector2(Position.X + (float)Math.Sin(-partAngle) * dist, Position.Y + (float)Math.Cos(-partAngle) * dist);

                return Matrix.Identity * Matrix.CreateScale(Constants.Ship.Scale) * Matrix.CreateRotationZ(Direction) * Matrix.CreateTranslation(new Vector3(partPosition, 0f));
            }
        }

        public float Speed { get; set; }
        public bool CanTeleport { get; set; }

        public void Kill(GameTime gameTime)
        {
            IsAlive = false;

            _partAngle[0] = Direction + MathHelper.ToRadians(120);
            _partAngle[1] = Direction - MathHelper.ToRadians(30);
            _partAngle[2] = Direction + MathHelper.ToRadians(30);
            _partAngle[3] = Direction - MathHelper.ToRadians(120);

            EndTimespan = gameTime.TotalGameTime;

            Initialize();
        }

        public void Trust(float scalar)
        {
            Speed += (Constants.Ship.MaxThrustIncrement * scalar);
            
            IsThrusting = true;
        }

        public void ApplyForces(Vector2 force)
        {
            // acceleration: change in velocity due to force f on object with mass m
            Vector2 dv = force / Mass;

            // new velocity if acceleration dv applied
            Vector2 new_v = Velocity + dv;

            // only apply Lorentz factor if acceleration increases speed
            if (new_v.Length() > Velocity.Length())
            {
                // maximum speed objects may reach (the "speed of light")
                const float c = 4;

                float b = 1 - (Velocity.LengthSquared()) / (c * c);
                if (b <= 0) b = 0f;

                float lorentz_factor = 1 / (float)Math.Sqrt(b);
                dv = dv / lorentz_factor;
            }

            // apply acceleration to object's velocity
            Velocity += dv;

            // Update:
            // Allow acceleration in the forward direction to change the direction
            // of v by using the direction of new_v (without the Lorentz factor)
            // with the magnitude of v (that applies the Lorentz factor).
            if (Velocity.Length() > 0)
            {
                new_v.Normalize();
                Velocity = new_v * Velocity.Length();
            }

        }

        public void StopThrust()
        {
            IsThrusting = false;
        }

        public void Move(bool playerControlled, GameTime gameTime)
        {
            if (!IsAlive)
                return;

            if (playerControlled)
            {
                Speed = Math.Min(Speed, Constants.Ship.MaxSpeed);

                Vector2 force = new Vector2((float)Math.Sin(Direction), -(float)Math.Cos(Direction));
                Velocity *= 0.95f;
                //if (Velocity.Length() < 0) Velocity = Vector2.Zero;

                if (IsThrusting)
                {
                    force *= Speed;
                    ApplyForces(force);
                }

                Position -= Velocity;
                Speed -= Constants.Ship.DecelerationRate;

                if (Speed <= 0f)
                {
                    Speed = 0f;
                }

                Position = WrapBounds(Position);
            }
            else
            {
                float tx = 0 - Position.X;
                float ty = 0 - Position.Y;
                float length = (float)Math.Sqrt(tx * tx + ty * ty);

                if (length > Constants.Ship.MaxSpeed)
                    Position = new Vector2(Position.X + Constants.Ship.MaxSpeed * tx / length, Position.Y + Constants.Ship.MaxSpeed * ty / length);
                else
                    Position = Vector2.Zero;

                Direction = MathHelper.WrapAngle(Direction);

                if (Direction > 0)
                    Direction -= .1f;
                else if (Direction < 0)
                    Direction += .1f;

                if (Direction < .1f && Direction > -.1f)
                    Direction = 0;

                if (Position == Vector2.Zero)
                {
                    if (!_spritesStarted)
                    {
                        _spritesStarted = true;

                        _pointSpriteGenerator.Start(Vector2.Zero, 5, .9f, 1.3f, .3f, 7, 9f, false, TimeSpan.FromSeconds(1000), 500, 100, gameTime.TotalGameTime);

                        WinTimespan = gameTime.TotalGameTime;
                    }
                }
            }
        }

        private bool _spritesStarted = false;

        internal void Teleport()
        {
            Velocity = Vector2.Zero;
        }
    }
}
